<template>
  <div class="dv-scroll-board">
    <div
      class="header"
      v-if="status.header.length"
      :style="`background-color: ${headerBGC};`"
    >
      <div
        class="header-item"
        v-for="(headerItem, i) in status.header"
        :key="`${headerItem}${i}`"
        :style="`
        height: ${headerHeight}px;
        line-height: ${headerHeight}px;
        width: ${status.widths[i]}px;
        color: ${headerTextColor};
        font-size: ${headerTextSize}px;
      `"
        :align="status.aligns[i]"
        v-html="headerItem"
      />
    </div>

    <div class="rows" :style="`height: ${height - (status.header.length ? headerHeight : 0)}px;`">
      <div class="row-item"
        v-for="(row, ri) in status.rows"
        :key="`${row.toString()}${row.scroll}`"
        :data-index="ri"
        :style="`
        height: ${status.heights[ri]}px;
        line-height: ${status.heights[ri]}px;
        background-color: ${props[row.rowIndex % 2 === 0 ? 'evenRowBGC' : 'oddRowBGC']};
        color: ${props[row.rowIndex % 2 === 0 ? 'evenRowTextColor' : 'oddRowTextColor']};
        font-size: ${headerTextSize}px;
        `">
        <div class="ceil"
          v-for="(ceil, ci) in row.ceils"
          :key="`${ceil}${ri}${ci}`"
          :style="`width: ${status.widths[ci]}px;`"
          :align="status.aligns[ci]"
          v-html="ceil"
        />
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { onUnmounted, reactive, toRefs, watch, onMounted,ref,nextTick } from 'vue';
import merge from 'lodash/merge';
import cloneDeep from 'lodash/cloneDeep';

const props = defineProps({
  width: {
    type: [Number,String],
    required: false,
    default:500
  },
  height: {
    type: [Number,String],
    required: false,
    default:300
  },
  rowNum: {
    type: Number,
    required: false,
    default:5
  },
  dataset:{
    type: Array,
    required: false,
    default:() => {
      const list = [];
      for(let i = 0; i < 10; i++){
        const row = [];
        for(let j = 0; j < 5; j++){
          row.push(`第${i+1}行第${j+1}列`);
        }
        list.push(row);
      }
      return list;
    }
  },
  headerHeight: {
    type: Number,
    required: false,
    default:60
  },
  header:{
    type: Array,
    required: false,
    default:() => {
      const headers = [];
      for(let i = 0; i < 5; i++){
        headers.push(`字段${i+1}`);
      }
      return headers;
    }
  },
  headerTextColor:{
    type:String,
    required: false,
    default:'#42A4E7FF'
  },
  headerTextSize:{
    type:Number,
    required: false,
    default:20
  },
  headerBGC:{
    type:String,
    required: false,
    default:'#1E2A3B5E'
  },
  oddRowBGC:{
    type:String,
    required: false,
    default:'#0F1C2D52'
  },
  oddRowTextColor:{
    type:String,
    required: false,
    default:'#F9FBFFFF'
  },
  evenRowBGC:{
    type:String,
    required: false,
    default:'#14253B4D'
  },
  evenRowTextColor:{
    type:String,
    required: false,
    default:'#FFFFFFFF'
  },
  columnWidth:{
    type:Array,
    required: false,
    default:[]
  },
  align:{
    type: Array,
    required: false,
    default:() => []
  },
  waitTime:{
    type: Number,
    required: false,
    default: 2
  },
  carousel:{
    type: String,
    required: false,
    default: 'single'
  },
  index:{
    type: Boolean,
    required: false,
    default: false
  },
  indexHeader:{
    type: String,
    required: false,
    default: '#'
  },
  hoverPause:{
    type: Boolean,
    required: false,
    default: true
  }
})

const status = reactive<any>({
  header: [],
  rowsData: [],
  rows: [
    {
      ceils: [],
      rowIndex: 0,
      scroll: 0
    }
  ],
  widths: [],
  heights: [0],
  avgHeight: 0,
  aligns: [],
  animationIndex: 0,
  animationHandler: 0,
  updater: 0,
  needCalc: false
})

const calcData = () => {
  calcHeaderData()
  calcRowsData()
  calcWidths()
  calcHeights()
  calcAligns()
  animation(true)
}

const calcHeaderData = () => {
  let { header,index,indexHeader } = props;
  if(index) header.unshift(indexHeader);
  status.header = header;
}

const calcRowsData = () => {
  let { dataset, headerBGC, rowNum, index } = props;
  if(index){
    dataset = dataset.map((row: any, i: number) => {
      row = [...row];
      const indexTag = `<span class="index" style="background-color: ${headerBGC};border-radius: 3px;padding: 0px 3px;">${i + 1}</span>`;
      row.unshift(indexTag);
      return row;
    })
  }
  dataset = dataset.map((ceils: any, i: number) => ({ ceils, rowIndex: i }));
  const rowLength = dataset.length;
  if (rowLength > rowNum && rowLength < 2 * rowNum){
    dataset = [...dataset, ...dataset];
  }
  dataset = dataset.map((d: any, i: number) => ({ ...d, scroll: i }));
  status.rowsData = dataset;
  status.rows = dataset;
}

const calcWidths = () => {
  const { rowsData } = status;
  const { columnWidth, header, index , width, height} = props;
  let columnNum = 1;
  if (rowsData[0]) {
    columnNum = (rowsData[0] as any).ceils.length
  } else if (header.length) {
    columnNum = header.length
  }
  const avgWidth = parseInt(width) / ( index ? columnNum + 1 : columnNum);
  const widths = new Array(columnNum).fill(avgWidth);
  status.widths = columnWidth.length ? columnWidth : widths;
}

const calcHeights = (onresize = false) => {
  const { header } = status;
  const { headerHeight, rowNum, dataset, width, height } = props;
  let allHeight = parseInt(height);
  if (header.length) allHeight -= headerHeight;
  const avgHeight = allHeight / rowNum
  status.avgHeight = avgHeight;
  if (!onresize) status.heights = new Array(dataset.length).fill(avgHeight)
}

const calcAligns = () => {
  const { header } = status;
  const columnNum = header.length;
  let aligns = new Array(columnNum).fill('center')
  const { align } = props;
  status.aligns = align.length ? align : aligns;
}

const animation = async (start = false) => {
  const { needCalc } = status

  if (needCalc) {
    calcRowsData()
    calcHeights()
    status.needCalc = false
  }
  let { avgHeight, animationIndex, rowsData, updater } = status
  const { waitTime, carousel, rowNum } = props;

  const rowLength = rowsData.length
  if (rowNum >= rowLength) return
  if (start) {
    await new Promise(resolve => setTimeout(resolve, waitTime * 1000))
    if (updater !== status.updater) return
  }
  const animationNum = carousel === 'single' ? 1 : rowNum
  let rows = rowsData.slice(animationIndex)
  rows.push(...rowsData.slice(0, animationIndex))
  status.rows = rows.slice(0, carousel === 'page' ? rowNum * 2 : rowNum + 1)
  status.heights = new Array(rowLength).fill(avgHeight)
  await new Promise(resolve => setTimeout(resolve, 300))
  if (updater !== status.updater) return
  status.heights.splice(0, animationNum, ...new Array(animationNum).fill(0))
  animationIndex += animationNum
  const back = animationIndex - rowLength
  if (back >= 0) animationIndex = back
  status.animationIndex = animationIndex
  status.animationHandler = setTimeout(animation, waitTime * 1000 - 300) as any
}

const stopAnimation = () => {
  status.updater = (status.updater + 1) % 999999
  if (!status.animationHandler) return
  clearTimeout(status.animationHandler)
}

const onRestart = async () => {
  try {
    stopAnimation()
    calcData()
  } catch (error) {
    console.log(error)
  }
}


const setDomInfo = () => {
  nextTick(() => {
    const dom:any = document.querySelector('.dv-scroll-board');
    if(!dom) return;
    const { width = 500,height = 300 } = props;
    dom.style.width = width + 'px';
    dom.style.height = height + 'px';
  })
}

watch(
  () => props.dataset,
  () => {
    onRestart()
  },
  { deep: true }
)

onMounted(() => {
  setDomInfo();
  onRestart();
})


onUnmounted(() => {
  stopAnimation();
})

</script>

<style lang="scss" scoped>
.dv-scroll-board {
  position: relative;
  width: 100%;
  height: 100%;
  color: #fff;

  .text {
    padding: 0 10px;
    box-sizing: border-box;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .header {
    display: flex;
    flex-direction: row;
    font-size: 15px;

    .header-item {
      transition: all 0.3s;
    }
  }

  .rows {
    overflow: hidden;

    .row-item {
      display: flex;
      font-size: 14px;
      transition: all 0.3s;
      overflow: hidden;

      .ceil{
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
      }
    }
  }
}
</style>
